Creating and Using Abstract Classes in Java
Course: Core Java Training
Topic: Creating and Using Abstract Classes
Course URL: Core Java Training Course
Demo URL: Register for a Course Demo
Website: JustAcademy Official Website
1. Introduction to Abstract Classes
Abstract classes are an important concept in Java object-oriented programming. They allow developers to define a common structure for related classes while leaving certain behaviors for individual subclasses to implement.
For example, a banking application may support savings accounts, current accounts, and fixed deposit accounts. All these accounts share common information, such as account number and account holder name. However, their interest calculations or transaction rules may differ.
Instead of duplicating common fields and methods in every account class, developers can create an abstract class named BankAccount. This class can contain shared functionality and declare abstract methods for operations that depend on the specific account type.
Abstract classes support abstraction, inheritance, code reuse, and runtime polymorphism. They are useful when several related classes share common characteristics but require different implementations for particular operations.
2. Learning Objectives
After studying this topic, you will be able to:
- Understand the purpose of abstract classes in Java.
- Declare abstract classes using the
abstract keyword.
- Create abstract methods and concrete methods.
- Extend abstract classes using inheritance.
- Implement abstract methods in concrete subclasses.
- Use constructors and instance variables in abstract classes.
- Understand abstract class references and runtime polymorphism.
- Compare abstract classes with interfaces.
- Identify common compilation errors and apply best practices.
3. What Is an Abstract Class?
An abstract class is a class declared using the abstract keyword. It represents a general or incomplete concept that cannot be instantiated directly.
An abstract class can contain abstract methods without implementations and concrete methods with implementations. It can also contain instance variables, constructors, static methods, and other regular class members.
Syntax
abstract class ClassName {
// Instance variables
// Constructor
abstract void abstractMethod();
void concreteMethod() {
System.out.println("This is a concrete method");
}
}
In this example:
abstract declares the class as abstract.
abstractMethod() declares a method without a method body.
concreteMethod() provides a complete implementation.
An abstract class cannot be instantiated directly with the new keyword. Instead, it is normally extended by subclasses that provide any required missing implementations.
4. Understanding the abstract Keyword
The abstract keyword indicates that a class or method is intended to be extended or implemented by a subclass.
4.1 Abstract Class
abstract class Animal {
void eat() {
System.out.println("Animal is eating");
}
}
This is a valid abstract class even though it does not contain an abstract method. A class may be declared abstract simply to prevent direct instantiation and establish a common parent for subclasses.
4.2 Abstract Method
abstract class Animal {
abstract void makeSound();
}
The method makeSound() has no body and ends with a semicolon. It specifies behavior that a concrete subclass must implement.
If a class declares an abstract method, the class itself must be declared abstract.
5. Difference Between Abstract and Concrete Classes
| Feature |
Abstract Class |
Concrete Class |
| Declaration |
Uses the abstract keyword. |
Does not use the abstract keyword. |
| Instantiation |
Cannot be instantiated directly. |
Can be instantiated if it has an accessible constructor. |
| Abstract methods |
May contain abstract methods. |
Cannot declare its own abstract methods. |
| Concrete methods |
Can contain implemented methods. |
Can contain implemented methods. |
| Constructors |
Can contain constructors. |
Can contain constructors. |
| Purpose |
Provides a shared, potentially incomplete foundation. |
Provides a complete implementation that can be instantiated. |
| Inheritance |
Can be extended by abstract or concrete classes. |
Can also be extended by subclasses. |
6. Creating Your First Abstract Class
Let's create an abstract class named Animal and a concrete subclass named Dog. The parent class defines a general behavior, while the subclass determines the actual implementation.
Java Program
abstract class Animal {
abstract void makeSound();
}
class Dog extends Animal {
@Override
void makeSound() {
System.out.println("Dog barks");
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.makeSound();
}
}
Output
Dog barks
Explanation
Animal is declared as an abstract class.
makeSound() is an abstract method without a body.
Dog inherits from Animal using extends.
- The
Dog class implements the abstract method.
- The program creates a
Dog object and invokes its method.
This example demonstrates how an abstract class establishes a common requirement while allowing subclasses to provide their own behavior.
7. Abstract Classes with Abstract and Concrete Methods
An abstract class can contain both abstract and concrete methods. This allows subclasses to reuse common functionality while implementing the behavior that differs between them.
Java Program
abstract class Vehicle {
String brand;
Vehicle(String brand) {
this.brand = brand;
}
void displayBrand() {
System.out.println("Brand: " + brand);
}
abstract void start();
}
class Car extends Vehicle {
Car(String brand) {
super(brand);
}
@Override
void start() {
System.out.println("Car starts with a key or button");
}
}
class Motorcycle extends Vehicle {
Motorcycle(String brand) {
super(brand);
}
@Override
void start() {
System.out.println("Motorcycle starts using its starting system");
}
}
public class Main {
public static void main(String[] args) {
Car car = new Car("Toyota");
Motorcycle motorcycle = new Motorcycle("Honda");
car.displayBrand();
car.start();
motorcycle.displayBrand();
motorcycle.start();
}
}
Output
Brand: Toyota
Car starts with a key or button
Brand: Honda
Motorcycle starts using its starting system
Explanation
The abstract class Vehicle contains a field named brand, a constructor, a concrete method named displayBrand(), and an abstract method named start().
The Car and Motorcycle classes inherit the shared brand-related functionality. Each class implements start() according to its own requirements.
The super(brand) statement invokes the constructor of the abstract parent class to initialize the inherited state. The abstract class constructor executes as part of creating a subclass object.
8. Rules for Abstract Classes in Java
- An abstract class must be declared with the
abstract keyword.
- An abstract class cannot be instantiated directly.
- An abstract class may contain zero or more abstract methods.
- A class declaring an abstract method must itself be abstract.
- A concrete subclass must implement all inherited abstract methods that remain unimplemented.
- If a subclass leaves any required abstract methods unimplemented, it must also be declared abstract.
- An abstract class can contain constructors, fields, static methods, and concrete methods.
- An abstract class can extend another class and implement one or more interfaces.
- A Java class can extend only one direct superclass.
- An abstract method cannot be declared
private, static, or final.
- An abstract method has no method body.
- A final class cannot be extended and therefore cannot be declared abstract at the same time.
9. Implementing Multiple Abstract Methods
An abstract class can declare multiple abstract methods when subclasses must provide several specific behaviors.
Java Program
abstract class Shape {
abstract double calculateArea();
abstract double calculatePerimeter();
void describe() {
System.out.println("This is a geometric shape");
}
}
class Rectangle extends Shape {
double length;
double width;
Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
@Override
double calculateArea() {
return length * width;
}
@Override
double calculatePerimeter() {
return 2 * (length + width);
}
}
public class Main {
public static void main(String[] args) {
Rectangle rectangle = new Rectangle(5, 3);
rectangle.describe();
System.out.println("Area: " + rectangle.calculateArea());
System.out.println("Perimeter: " + rectangle.calculatePerimeter());
}
}
Output
This is a geometric shape
Area: 15.0
Perimeter: 16.0
Explanation
The abstract class Shape requires subclasses to provide implementations for calculating area and perimeter. The Rectangle class implements both methods using its length and width.
The concrete method describe() is inherited and reused without being rewritten in the subclass.
10. Using Abstract Class References
Although an abstract class cannot be instantiated directly, it can be used as the declared type of a reference variable. That reference can point to an object created from a concrete subclass.
Java Program
abstract class Employee {
abstract void calculateSalary();
}
class FullTimeEmployee extends Employee {
@Override
void calculateSalary() {
System.out.println("Calculating monthly salary");
}
}
class ContractEmployee extends Employee {
@Override
void calculateSalary() {
System.out.println("Calculating contract payment");
}
}
public class Main {
public static void main(String[] args) {
Employee employee1 = new FullTimeEmployee();
Employee employee2 = new ContractEmployee();
employee1.calculateSalary();
employee2.calculateSalary();
}
}
Output
Calculating monthly salary
Calculating contract payment
Here, Employee is the reference type, while the actual objects are FullTimeEmployee and ContractEmployee. Java invokes the overridden method according to the actual object's runtime class. This behavior is called runtime polymorphism.
11. Abstract Classes and Constructors
Abstract classes can have constructors to initialize common fields shared by their subclasses. These constructors are not called to instantiate the abstract class directly. Instead, they execute during the construction of a subclass object.
Java Program
abstract class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
void displayDetails() {
System.out.println("Name: " + name);
System.out.println("Age: " + age);
}
abstract void performRole();
}
class Student extends Person {
Student(String name, int age) {
super(name, age);
}
@Override
void performRole() {
System.out.println("Role: Learning");
}
}
public class Main {
public static void main(String[] args) {
Student student = new Student("Amit", 20);
student.displayDetails();
student.performRole();
}
}
Output
Name: Amit
Age: 20
Role: Learning
The Person constructor initializes the common fields. The Student constructor calls it using super(name, age). The subclass implements the abstract method performRole().
12. Abstract Classes and Method Overriding
Method overriding occurs when a subclass provides its own implementation of an inherited instance method with a compatible method signature. Abstract methods are designed to be implemented by concrete subclasses.
abstract class Appliance {
abstract void turnOn();
void showCategory() {
System.out.println("Home appliance");
}
}
class Fan extends Appliance {
@Override
void turnOn() {
System.out.println("Fan starts rotating");
}
}
class WashingMachine extends Appliance {
@Override
void turnOn() {
System.out.println("Washing machine starts its cycle");
}
}
Both subclasses implement the same abstract method but provide different behaviors. They also inherit the concrete method showCategory().
The @Override annotation is recommended because it allows the compiler to detect mistakes in method signatures.
13. Abstract Classes vs. Interfaces
Abstract classes and interfaces both support abstraction, but they are used for different design purposes. Abstract classes are useful for sharing state and implementation among closely related classes. Interfaces are useful for defining capabilities that different classes can implement.
| Feature |
Abstract Class |
Interface |
| Declaration |
Uses abstract class. |
Uses the interface keyword. |
| Inheritance keyword |
A subclass uses extends. |
A class uses implements. |
| Instance fields |
Can contain instance variables. |
Fields are implicitly public static final. |
| Constructors |
Can have constructors. |
Cannot have constructors. |
| Implemented methods |
Can contain concrete instance methods. |
Can contain default, static, and private methods subject to Java version rules. |
| Abstract methods |
Can declare abstract methods. |
Can declare abstract instance methods. |
| Inheritance limit |
A class can extend only one class. |
A class can implement multiple interfaces. |
| Best use |
Sharing common state and implementation among related classes. |
Defining a capability or contract for different classes. |
When Should You Use an Abstract Class?
Use an abstract class when related classes share state, constructors, or implemented behavior. For example, a Vehicle class may store common registration information and provide shared methods.
When Should You Use an Interface?
Use an interface when different classes need to support the same capability. For example, an interface named Printable could be implemented by invoices, reports, and photographs.
14. Real-World Applications of Abstract Classes
- Banking systems: A common account class can store account information while different account types implement their own rules.
- Payment systems: A payment class can store transaction information while subclasses implement different payment methods.
- Employee management: A base employee class can store common details while different employee types calculate compensation differently.
- Vehicle management: A vehicle class can provide common information while cars, motorcycles, and buses implement specific operations.
- Graphics applications: A shape class can define common properties while circles and rectangles implement their own area calculations.
- Notification systems: A notification class can hold recipient information while email and SMS subclasses provide delivery-specific behavior.
- File processing: A shared processor class can define common workflow steps while subclasses handle different file formats.
15. Practical Example: Payment Processing System
This example demonstrates how an abstract class can combine common payment information with different payment implementations.
Java Program
abstract class Payment {
double amount;
Payment(double amount) {
this.amount = amount;
}
void showAmount() {
System.out.println("Payment amount: " + amount);
}
abstract void processPayment();
}
class UpiPayment extends Payment {
UpiPayment(double amount) {
super(amount);
}
@Override
void processPayment() {
System.out.println("Processing UPI payment");
}
}
class CardPayment extends Payment {
CardPayment(double amount) {
super(amount);
}
@Override
void processPayment() {
System.out.println("Processing card payment");
}
}
public class Main {
public static void main(String[] args) {
Payment payment1 = new UpiPayment(1500.00);
Payment payment2 = new CardPayment(2500.00);
payment1.showAmount();
payment1.processPayment();
payment2.showAmount();
payment2.processPayment();
}
}
Output
Payment amount: 1500.0
Processing UPI payment
Payment amount: 2500.0
Processing card payment
Step-by-Step Explanation
- The abstract class
Payment stores the common payment amount.
- The constructor initializes the amount.
- The concrete method
showAmount() displays shared information.
- The abstract method
processPayment() defines a behavior that subclasses must implement.
- The
UpiPayment and CardPayment classes extend Payment.
- Each subclass implements
processPayment() differently.
- Abstract-class references demonstrate runtime polymorphism.
Note: This example demonstrates object-oriented design only. A real payment system requires secure payment-provider integration, transaction validation, error handling, and suitable security controls.
16. Common Errors When Using Abstract Classes
| Error |
Reason |
Solution |
| Creating an abstract-class object |
Abstract classes cannot be instantiated directly. |
Create an object of a concrete subclass. |
| Forgetting an abstract method |
A concrete subclass must implement all inherited abstract methods that remain unimplemented. |
Implement the methods or declare the subclass abstract. |
| Declaring an abstract method in a concrete class |
A class containing an abstract method must itself be abstract. |
Declare the class abstract or provide a method body. |
| Giving an abstract method a body |
Abstract methods declare behavior without providing an implementation. |
Remove the body or declare the method concrete. |
| Using an incompatible method signature |
The subclass method does not correctly override the inherited method. |
Use a compatible method signature and return type. |
| Declaring an abstract method as private |
A private method cannot be overridden by subclasses. |
Use an appropriate non-private access level. |
| Declaring an abstract method as static or final |
Static methods are not overridden polymorphically, and final methods cannot be overridden. |
Remove the conflicting modifier. |
17. Common Mistakes and Corrections
Mistake 1: Instantiating an Abstract Class
abstract class Animal {
abstract void makeSound();
}
// Invalid:
// Animal animal = new Animal();
Correction: Create an object of a concrete subclass.
class Cat extends Animal {
@Override
void makeSound() {
System.out.println("Cat meows");
}
}
// Valid:
Animal animal = new Cat();
animal.makeSound();
Mistake 2: Not Implementing Required Methods
abstract class Shape {
abstract void draw();
}
// Invalid if Circle is declared as a concrete class:
// class Circle extends Shape {
// }
Correction: Implement draw() in Circle, or declare Circle as abstract.
Mistake 3: Incorrect Method Signature
If the parent declares abstract void draw();, a subclass must provide a compatible implementation. A method with a different parameter list does not implement the original abstract method.
abstract class Shape {
abstract void draw();
}
class Circle extends Shape {
@Override
void draw() {
System.out.println("Drawing a circle");
}
}
18. Best Practices for Abstract Classes
- Use abstract classes when related classes share meaningful state or implementation.
- Keep the parent class focused on common responsibilities.
- Declare methods abstract when subclasses genuinely need different implementations.
- Use meaningful class and method names, such as
Payment, calculateArea(), and processPayment().
- Use
@Override when implementing or overriding inherited methods.
- Choose appropriate access modifiers to protect internal state and expose intended behavior.
- Initialize shared state through constructors when appropriate.
- Prefer interfaces for capabilities that may be shared by unrelated classes.
- Avoid unnecessarily deep inheritance hierarchies because they can make code harder to maintain.
- Ensure that each concrete subclass fulfills the behavior promised by its abstract parent class.
19. Practice Exercises
Exercise 1: Animal Sounds
Create an abstract class named Animal with an abstract method makeSound(). Create Dog, Cat, and Cow subclasses with different implementations.
Exercise 2: Shape Calculator
Create an abstract class named Shape with abstract methods calculateArea() and calculatePerimeter(). Implement these methods in Circle and Rectangle.
Exercise 3: Employee Salary
Create an abstract class named Employee with fields for employee name and ID. Add an abstract method named calculateSalary() and implement it in full-time and part-time employee classes.
Exercise 4: Notification Service
Create an abstract class named Notification with a recipient field and an abstract method named sendNotification(). Implement email and SMS notification subclasses.
Exercise 5: Abstract Class and Polymorphism
Create an abstract class named Appliance with an abstract method named operate(). Create multiple subclasses and call their methods using an array of Appliance references.
20. Mini Project: Employee Management System
Project Objective: Build a simple employee management program using abstract classes, inheritance, constructors, method overriding, and runtime polymorphism.
Requirements
- Create an abstract class named
Employee.
- Add fields for employee name and employee ID.
- Create a constructor to initialize common employee details.
- Add a concrete method named
displayDetails().
- Add an abstract method named
calculateSalary().
- Create concrete subclasses named
FullTimeEmployee and PartTimeEmployee.
- Implement salary calculations differently in each subclass.
- Create objects and display employee details and calculated salaries.
Sample Implementation
abstract class Employee {
String name;
int employeeId;
Employee(String name, int employeeId) {
this.name = name;
this.employeeId = employeeId;
}
void displayDetails() {
System.out.println("Name: " + name);
System.out.println("Employee ID: " + employeeId);
}
abstract double calculateSalary();
}
class FullTimeEmployee extends Employee {
double monthlySalary;
FullTimeEmployee(String name, int employeeId, double monthlySalary) {
super(name, employeeId);
this.monthlySalary = monthlySalary;
}
@Override
double calculateSalary() {
return monthlySalary;
}
}
class PartTimeEmployee extends Employee {
int hoursWorked;
double hourlyRate;
PartTimeEmployee(String name, int employeeId,
int hoursWorked, double hourlyRate) {
super(name, employeeId);
this.hoursWorked = hoursWorked;
this.hourlyRate = hourlyRate;
}
@Override
double calculateSalary() {
return hoursWorked * hourlyRate;
}
}
public class Main {
public static void main(String[] args) {
Employee employee1 =
new FullTimeEmployee("Riya", 101, 40000);
Employee employee2 =
new PartTimeEmployee("Rahul", 102, 80, 250);
employee1.displayDetails();
System.out.println("Salary: " + employee1.calculateSalary());
employee2.displayDetails();
System.out.println("Salary: " + employee2.calculateSalary());
}
}
Sample Output
Name: Riya
Employee ID: 101
Salary: 40000.0
Name: Rahul
Employee ID: 102
Salary: 20000.0
Learning Outcome: This project demonstrates how an abstract class can define shared employee information and a salary-calculation contract while allowing each employee type to calculate its salary differently.
21. Interview Questions and Answers
Q1. What is an abstract class in Java?
An abstract class is declared using the abstract keyword. It cannot be instantiated directly and can provide shared implementations and abstract methods for subclasses.
Q2. Can an abstract class contain concrete methods?
Yes. An abstract class can contain both abstract methods and concrete methods with implementations.
Q3. Can we create an object of an abstract class?
No. Direct instantiation is not allowed. However, a reference declared with an abstract class type can refer to an object of a concrete subclass.
Q4. Can an abstract class have a constructor?
Yes. Its constructor initializes inherited state when a subclass object is created.
Q5. Can an abstract class have no abstract methods?
Yes. A class can be abstract even when all its methods have implementations. This can prevent direct instantiation of a general-purpose parent class.
Q6. What happens if a subclass does not implement all abstract methods?
The subclass must also be abstract unless the required methods have already been implemented by an intermediate class.
Q7. Can an abstract class contain static methods?
Yes. Static methods can belong to an abstract class and can be called using the class name. Static methods are not overridden polymorphically like instance methods.
Q8. Can an abstract class extend another abstract class?
Yes. It can extend another abstract class and leave some abstract methods for a later concrete subclass to implement.
Q9. What is the difference between an abstract class and an interface?
An abstract class can maintain instance state, define constructors, and provide shared implementations. An interface primarily defines a contract or capability and allows a class to implement multiple interfaces.
Q10. Why is abstraction useful?
Abstraction separates the general definition of behavior from implementation details. It helps manage complexity and makes related classes easier to extend and maintain.
Q11. Can an abstract method be private?
No. A private method is not inherited for overriding, so an abstract method cannot be declared private.
Q12. Can an abstract method be final?
No. The final modifier prevents overriding, which conflicts with the purpose of an abstract method.
Q13. What is the role of @Override?
The annotation tells the compiler that a method is intended to override an inherited method. It helps identify incorrect signatures and other mistakes.
Q14. How do abstract classes support polymorphism?
A reference declared with an abstract parent type can refer to different concrete subclass objects. Overridden instance methods are selected according to the actual object's runtime type.
Q15. When should we choose an abstract class?
Choose an abstract class when related classes need shared implementation, common instance fields, constructor-based initialization, and behaviors that subclasses implement differently.
22. Quick Revision Notes
- The
abstract keyword declares an abstract class or method.
- An abstract class cannot be instantiated directly.
- An abstract method has no method body and ends with a semicolon.
- A class that declares an abstract method must itself be abstract.
- An abstract class can contain abstract and concrete methods.
- Abstract classes can have constructors, fields, and static methods.
- Subclasses inherit common functionality and implement required abstract methods.
- A subclass with unimplemented abstract methods must remain abstract.
- Abstract-class references can refer to concrete subclass objects.
- Overridden instance methods support runtime polymorphism.
- Abstract classes are useful for sharing common state and behavior among related classes.
- Interfaces are useful for defining capabilities that can be shared across different class hierarchies.
23. Conclusion
Abstract classes are a fundamental feature of Java that help developers create reusable and maintainable object-oriented designs. They provide a common structure for related classes, allow shared code to be written once, and require subclasses to supply specific implementations where necessary.
By combining abstract methods, concrete methods, constructors, inheritance, and polymorphism, developers can design flexible applications such as payment systems, employee management tools, vehicle applications, and shape calculators.
Practise creating abstract classes, implementing subclasses, and using abstract-class references to invoke overridden methods to strengthen your understanding of this topic.
24. Additional Learning Resources